/*
REWRITTEN FROM SCRATCH BY PUSSYWIZARD, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "trial_of_the_crusader.h"
#include "SpellScript.h"
#include "Player.h"

enum eAIs
{
	AI_MELEE	= 0,
	AI_RANGED	= 1,
	AI_HEALER	= 2,
	AI_PET		= 3,
};

enum eSharedSpells
{
	SPELL_ANTI_AOE								= 68595,
	SPELL_PVP_TRINKET							= 65547,
};

struct boss_faction_championsAI : public ScriptedAI
{
	boss_faction_championsAI(Creature* pCreature, uint32 aitype) : ScriptedAI(pCreature)
	{
		pInstance = pCreature->GetInstanceScript();
		me->SetReactState(REACT_PASSIVE);
		mAIType = aitype;
		threatTimer = 2000;
		powerTimer = 1000;
	}

	InstanceScript* pInstance;
	uint32 mAIType;
	uint32 threatTimer;
	uint32 powerTimer;

	void EnterCombat(Unit* /*who*/)
	{
		me->SetInCombatWithZone();
		RecalculateThreat();
		if( pInstance )
			pInstance->SetData(TYPE_FACTION_CHAMPIONS_START, 0);
	}

	void AttackStart(Unit* who)
	{
		if( !who )
			return;

		if( mAIType == AI_MELEE || mAIType == AI_PET )
			UnitAI::AttackStart(who);
		else
			UnitAI::AttackStartCaster(who, 18.5f);
	}

	float GetThreatMod(float dist, float armor, uint32 health, uint32 /*maxhealth*/, Unit* target)
	{
		/*float mod_health = ((float)health)/maxhealth;
		if (mod_health < 0.4f) mod_health = 0.4f;
		float unimportant_dist = (mAIType == AI_MELEE || mAIType == AI_PET ? 5.0f : 20.0f);
		if (dist > unimportant_dist)
			dist -= unimportant_dist; // compensate melee range
		else
			dist = 0.0f;
		float mod_dist = 25.0f/(25.0f + dist);
		float mod_armor = (mAIType == AI_MELEE || mAIType == AI_PET) ? armor / 16635.0f : 0.0f;
		return mod_dist / ((0.15f+mod_armor)*mod_health);*/

		// TC:
        /*float dist_mod = (mAIType == AI_MELEE || mAIType == AI_PET) ? 15.0f / (15.0f + dist) : 1.0f;
        float armor_mod = (mAIType == AI_MELEE || mAIType == AI_PET) ? armor / 16635.0f : 0.0f;
        float eh = (health + 1) * (1.0f + armor_mod);
        return dist_mod * 30000.0f / eh;*/

		// third try:
		float unimportant_dist = (mAIType == AI_MELEE || mAIType == AI_PET ? 5.0f : 35.0f);
		if (dist > unimportant_dist) dist -= unimportant_dist; else dist = 0.0f;
		const float dist_factor = (mAIType == AI_MELEE || mAIType == AI_PET ? 15.0f : 25.0f);
		float mod_dist = dist_factor/(dist_factor + dist); // 0.2 .. 1.0
		float mod_health = health > 40000 ? 2.0f : (60000-health)/10000.0f; // 2.0 .. 6.0
		float mod_armor = (mAIType == AI_MELEE || mAIType == AI_PET) ? Unit::CalcArmorReducedDamage(me, target, 10000, NULL)/10000.0f : 1.0f;
		return mod_dist * mod_health * mod_armor;
	}

	void RecalculateThreat()
	{
		ThreatContainer::StorageType const& tList = me->getThreatManager().getThreatList();
		for( ThreatContainer::StorageType::const_iterator itr = tList.begin(); itr != tList.end(); ++itr )
		{
			Unit* pUnit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid());
			if( pUnit && pUnit->GetTypeId() == TYPEID_PLAYER && me->getThreatManager().getThreat(pUnit) )
			{
				float threatMod = GetThreatMod(me->GetDistance2d(pUnit), (float)pUnit->GetArmor(), pUnit->GetHealth(), pUnit->GetMaxHealth(), pUnit);
				me->getThreatManager().modifyThreatPercent(pUnit, -100);
				//me->getThreatManager().doAddThreat(pUnit, 10000000.0f * threatMod);
				if (HostileReference* ref = me->getThreatManager().getOnlineContainer().getReferenceByTarget(pUnit))
					ref->addThreat(10000000.0f * threatMod);
			}
		}
	}

	void EventMapGCD(EventMap &e, uint32 delay, uint32 gcd = 0)
	{
		e.DelayEventsToMax(delay, gcd);
	}

	void JustDied(Unit* /*pKiller*/)
	{
		if( pInstance && mAIType != AI_PET )
			pInstance->SetData(TYPE_FACTION_CHAMPIONS, DONE);
	}

	void KilledUnit(Unit* who)
	{
		if( pInstance )
			pInstance->SetData(TYPE_FACTION_CHAMPIONS_PLAYER_DIED, 1);
	}

	void EnterEvadeMode()
	{
		if( pInstance )
			pInstance->SetData(TYPE_FAILED, 0);
	}

	bool IsCCed()
	{
		// check for stun, fear, etc.
		// for casting, silence, disarm check individually in the ai
		if( me->isFeared() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED) )
		{
			if( !IsHeroic() )
				return true;
			if( me->HasSpellCooldown(SPELL_PVP_TRINKET) )
				return true;
			else
			{
				me->CastSpell(me, SPELL_PVP_TRINKET, false);
				me->AddSpellCooldown(SPELL_PVP_TRINKET, 0, 0);
			}
		}
		return false;
	}

	Creature* SelectTarget_MostHPLostFriendlyMissingBuff(uint32 spell, float range)
	{
		std::list<Creature *> lst = DoFindFriendlyMissingBuff(range, spell);
		if( lst.empty() )
			return NULL;
		std::list<Creature *>::const_iterator iter = lst.begin();
		uint32 lowestHP = (*iter)->GetMaxHealth() - (*iter)->GetHealth();
		for( std::list<Creature *>::const_iterator itr = lst.begin(); itr != lst.end(); ++itr )
			if( ((*itr)->GetMaxHealth() - (*itr)->GetHealth()) > lowestHP )
			{
				iter = itr;
				lowestHP = (*itr)->GetMaxHealth() - (*itr)->GetHealth();
			}
		return (*iter);
	}

	uint32 EnemiesInRange(float distance)
	{
		ThreatContainer::StorageType const& tList = me->getThreatManager().getThreatList();
		uint32 count = 0;
		Unit *target;
		for( ThreatContainer::StorageType::const_iterator iter = tList.begin(); iter != tList.end(); ++iter )
		{
			target = ObjectAccessor::GetUnit((*me), (*iter)->getUnitGuid());
			if( target && me->GetDistance2d(target) < distance )
				++count;
		}
		return count;
	}

	Unit* SelectEnemyCaster(bool casting, float range)
	{
		ThreatContainer::StorageType const& tList = me->getThreatManager().getThreatList();
		Unit *target;
		for( ThreatContainer::StorageType::const_iterator iter = tList.begin(); iter != tList.end(); ++iter )
		{
			target = ObjectAccessor::GetUnit((*me), (*iter)->getUnitGuid());
			if( target && target->getPowerType() == POWER_MANA && (!casting || target->HasUnitState(UNIT_STATE_CASTING)) && me->GetExactDist(target) <= range )
				return target;
		}
		return NULL;
	}

	void UpdateAI(uint32 diff)
	{
		if (!me->IsInCombat())
			return;

		if( threatTimer <= diff )
		{
			RecalculateThreat();
			threatTimer = urand(8750, 9250);
		}
		else
			threatTimer -= diff;

		if( me->getPowerType() == POWER_MANA )
		{
			if( powerTimer <= diff )
			{
				me->ModifyPower(POWER_MANA, me->GetMaxPower(POWER_MANA)/3);
				powerTimer = 4000;
			}
			else
				powerTimer -= diff;
		}
		else if( me->getPowerType() == POWER_ENERGY )
		{
			if( powerTimer <= diff )
			{
				me->ModifyPower(POWER_ENERGY, me->GetMaxPower(POWER_ENERGY)/3);
				powerTimer = 1000;
			}
			else
				powerTimer -= diff;
		}
	}
};

enum eDruidSpells
{
	SPELL_LIFEBLOOM		 = 66093,
	SPELL_NOURISH		   = 66066,
	SPELL_REGROWTH		  = 66067,
	SPELL_REJUVENATION	  = 66065,
	SPELL_THORNS			= 66068,
	SPELL_TRANQUILITY	   = 66086,
	SPELL_BARKSKIN		  = 65860,
	SPELL_NATURE_GRASP	  = 66071,
};

enum eDruidEvents
{
	EVENT_SPELL_LIFEBLOOM = 1,
	EVENT_SPELL_NOURISH,
	EVENT_SPELL_REGROWTH,
	EVENT_SPELL_REJUVENATION,
	EVENT_SPELL_THORNS,
	EVENT_SPELL_TRANQUILITY,
	EVENT_SPELL_NATURE_GRASP,
	EVENT_SPELL_BARKSKIN = 101,
};

class npc_toc_druid : public CreatureScript
{
public:
	npc_toc_druid() : CreatureScript("npc_toc_druid") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_druidAI (pCreature);
	}

	struct npc_toc_druidAI : public boss_faction_championsAI
	{
		npc_toc_druidAI(Creature* pCreature) : boss_faction_championsAI(pCreature, AI_HEALER)
		{
			SetEquipmentSlots(false, 51799, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_LIFEBLOOM, urand(5000,15000));
			events.RescheduleEvent(EVENT_SPELL_NOURISH, urand(5000,15000));
			events.RescheduleEvent(EVENT_SPELL_REGROWTH, urand(5000,15000));
			events.RescheduleEvent(EVENT_SPELL_REJUVENATION, urand(5000,15000));
			events.RescheduleEvent(EVENT_SPELL_TRANQUILITY, urand(25000,40000));
			events.RescheduleEvent(EVENT_SPELL_BARKSKIN, 10000);
			events.RescheduleEvent(EVENT_SPELL_THORNS, urand(5000,15000));
			events.RescheduleEvent(EVENT_SPELL_NATURE_GRASP, urand(5000,15000));
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) || IsCCed());
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_LIFEBLOOM:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_LIFEBLOOM, 40.0f) )
						me->CastSpell(target, SPELL_LIFEBLOOM, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_NOURISH:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_NOURISH, 40.0f) )
						me->CastSpell(target, SPELL_NOURISH, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_REGROWTH:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_REGROWTH, 40.0f) )
						me->CastSpell(target, SPELL_REGROWTH, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_REJUVENATION:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_REJUVENATION, 40.0f) )
						me->CastSpell(target, SPELL_REJUVENATION, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_THORNS:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_THORNS, 30.0f) )
						me->CastSpell(target, SPELL_THORNS, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_TRANQUILITY:
					me->CastSpell(me, SPELL_TRANQUILITY, false);
					events.RepeatEvent(urand(120000,180000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_BARKSKIN:
					if( HealthBelowPct(50) )
					{
						me->CastSpell(me, SPELL_BARKSKIN, false);
						events.RepeatEvent(60000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_NATURE_GRASP:
					me->CastSpell(me, SPELL_NATURE_GRASP, false);
					events.RepeatEvent(60000);
					EventMapGCD(events, 1500);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

enum eShamanSpells
{
	SPELL_HEALING_WAVE		  = 66055,
	SPELL_RIPTIDE			   = 66053,
	SPELL_SPIRIT_CLEANSE		= 66056,
	SPELL_HEROISM			   = 65983,
	SPELL_BLOODLUST			 = 65980,
	SPELL_HEX				   = 66054,
	SPELL_EARTH_SHIELD		  = 66063,
	SPELL_EARTH_SHOCK		   = 65973,
	AURA_EXHAUSTION			 = 57723,
	AURA_SATED				  = 57724,
};

enum eShamanEvents
{
	EVENT_SPELL_HEALING_WAVE = 1,
	EVENT_SPELL_RIPTIDE,
	EVENT_SPELL_SPIRIT_CLEANSE,
	EVENT_SPELL_HEROISM_OR_BLOODLUST,
	EVENT_SPELL_HEX,
	EVENT_SPELL_EARTH_SHIELD,
	EVENT_SPELL_EARTH_SHOCK,
};

class npc_toc_shaman : public CreatureScript
{
public:
	npc_toc_shaman() : CreatureScript("npc_toc_shaman") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_shamanAI (pCreature);
	}

	struct npc_toc_shamanAI : public boss_faction_championsAI
	{
		npc_toc_shamanAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_HEALER)
		{
			SetEquipmentSlots(false, 49992, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_HEALING_WAVE, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_RIPTIDE, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_SPIRIT_CLEANSE, urand(10000,15000));
			events.RescheduleEvent(EVENT_SPELL_HEROISM_OR_BLOODLUST, urand(25000,40000));
			events.RescheduleEvent(EVENT_SPELL_HEX, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_EARTH_SHIELD, urand(15000,25000));
			events.RescheduleEvent(EVENT_SPELL_EARTH_SHOCK, urand(3000,10000));
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) || IsCCed());
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_HEALING_WAVE:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_HEALING_WAVE, 40.0f) )
						me->CastSpell(target, SPELL_HEALING_WAVE, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_RIPTIDE:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_RIPTIDE, 40.0f) )
						me->CastSpell(target, SPELL_RIPTIDE, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_SPIRIT_CLEANSE:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_SPIRIT_CLEANSE, 40.0f) )
						me->CastSpell(target, SPELL_SPIRIT_CLEANSE, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_HEROISM_OR_BLOODLUST:
					if( me->GetEntry() == NPC_ALLIANCE_SHAMAN_RESTORATION )
						me->CastSpell((Unit*)NULL, SPELL_HEROISM, true);
					else
						me->CastSpell((Unit*)NULL, SPELL_BLOODLUST, true);
					events.RepeatEvent(600000);
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_HEX:
					if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 20.0f, true) )
						me->CastSpell(target, SPELL_HEX, false);
					events.RepeatEvent(45000);
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_EARTH_SHIELD:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_EARTH_SHIELD, 40.0f) )
						me->CastSpell(target, SPELL_EARTH_SHIELD, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_EARTH_SHOCK:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_EARTH_SHOCK, false);
					events.RepeatEvent(urand(5000,10000));
					EventMapGCD(events, 1500);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

enum ePaladinSpells
{
	SPELL_HAND_OF_FREEDOM	 = 68757,
	SPELL_BUBBLE			  = 66010,
	SPELL_CLEANSE			 = 66116,
	SPELL_FLASH_OF_LIGHT	  = 66113,
	SPELL_HOLY_LIGHT		  = 66112,
	SPELL_HOLY_SHOCK		  = 66114,
	SPELL_HAND_OF_PROTECTION  = 66009,
	SPELL_HAMMER_OF_JUSTICE   = 66613,
};

enum ePaladinEvents
{
	EVENT_SPELL_HAND_OF_FREEDOM = 1,
	EVENT_SPELL_BUBBLE,
	EVENT_SPELL_CLEANSE,
	EVENT_SPELL_FLASH_OF_LIGHT,
	EVENT_SPELL_HOLY_LIGHT,
	EVENT_SPELL_HOLY_SHOCK,
	EVENT_SPELL_HAND_OF_PROTECTION,
	EVENT_SPELL_HAMMER_OF_JUSTICE,
};

class npc_toc_paladin : public CreatureScript
{
public:
	npc_toc_paladin() : CreatureScript("npc_toc_paladin") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_paladinAI (pCreature);
	}

	struct npc_toc_paladinAI : public boss_faction_championsAI
	{
		npc_toc_paladinAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_HEALER)
		{
			SetEquipmentSlots(false, 50771, 47079, EQUIP_NO_CHANGE);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_HAND_OF_FREEDOM, urand(10000,15000));
			events.RescheduleEvent(EVENT_SPELL_BUBBLE, 10000);
			events.RescheduleEvent(EVENT_SPELL_CLEANSE, urand(10000,15000));
			events.RescheduleEvent(EVENT_SPELL_FLASH_OF_LIGHT, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_HOLY_LIGHT, urand(5000,15000));
			events.RescheduleEvent(EVENT_SPELL_HOLY_SHOCK, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_HAND_OF_PROTECTION, urand(20000,35000));
			events.RescheduleEvent(EVENT_SPELL_HAMMER_OF_JUSTICE, urand(10000,20000));
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) || IsCCed());
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_HAND_OF_FREEDOM:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_HAND_OF_FREEDOM, 30.0f) )
						me->CastSpell(target, SPELL_HAND_OF_FREEDOM, false);
					events.RepeatEvent(25000);
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_BUBBLE:
					if( HealthBelowPct(25) )
					{
						me->CastSpell(me, SPELL_BUBBLE, false);
						events.RepeatEvent(300000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(10000);
					break;
				case EVENT_SPELL_CLEANSE:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_CLEANSE, 40.0f) )
						me->CastSpell(target, SPELL_CLEANSE, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_FLASH_OF_LIGHT:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_FLASH_OF_LIGHT, 40.0f) )
						me->CastSpell(target, SPELL_FLASH_OF_LIGHT, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_HOLY_LIGHT:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_HOLY_LIGHT, 40.0f) )
						me->CastSpell(target, SPELL_HOLY_LIGHT, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_HOLY_SHOCK:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_HOLY_SHOCK, 40.0f) )
						me->CastSpell(target, SPELL_HOLY_SHOCK, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_HAND_OF_PROTECTION:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_HAND_OF_PROTECTION, 40.0f) )
					{
						me->CastSpell(target, SPELL_HAND_OF_PROTECTION, false);
						events.RepeatEvent(300000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(10000);
					break;
				case EVENT_SPELL_HAMMER_OF_JUSTICE:
					if( Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 15.0f, true) )
					{
						me->CastSpell(target, SPELL_HAMMER_OF_JUSTICE, false);
						events.RepeatEvent(40000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(10000);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

enum ePriestSpells
{
	SPELL_RENEW			 = 66177,
	SPELL_SHIELD			= 66099,
	SPELL_FLASH_HEAL		= 66104,
	SPELL_DISPEL			= 65546,
	SPELL_MANA_BURN		 = 66100,
	SPELL_PSYCHIC_SCREAM	= 65543,
};

enum ePriestEvents
{
	EVENT_SPELL_RENEW = 1,
	EVENT_SPELL_SHIELD,
	EVENT_SPELL_FLASH_HEAL,
	EVENT_SPELL_MANA_BURN,
	EVENT_SPELL_DISPEL = 100,
	EVENT_SPELL_PSYCHIC_SCREAM = 101,
};

class npc_toc_priest : public CreatureScript
{
public:
	npc_toc_priest() : CreatureScript("npc_toc_priest") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_priestAI (pCreature);
	}

	struct npc_toc_priestAI : public boss_faction_championsAI
	{
		npc_toc_priestAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_HEALER)
		{
			SetEquipmentSlots(false, 49992, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_RENEW, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_SHIELD, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_FLASH_HEAL, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_DISPEL, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_MANA_BURN, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_PSYCHIC_SCREAM, 10000);
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) || IsCCed());
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_RENEW:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_RENEW, 40.0f) )
						me->CastSpell(target, SPELL_RENEW, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_SHIELD:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_SHIELD, 40.0f) )
						me->CastSpell(target, SPELL_SHIELD, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_FLASH_HEAL:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_FLASH_HEAL, 40.0f) )
						me->CastSpell(target, SPELL_FLASH_HEAL, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_DISPEL:
					if( Unit* target = (urand(0,1) ? SelectTarget(SELECT_TARGET_TOPAGGRO, 0, 30.0f, true) : SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_DISPEL, 40.0f)) )
						me->CastSpell(target, SPELL_DISPEL, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_MANA_BURN:
					if( Unit* target = SelectEnemyCaster(false, 30.0f) )
					{
						me->CastSpell(target, SPELL_MANA_BURN, false);
						events.RepeatEvent(urand(10000,15000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_PSYCHIC_SCREAM:
					if( HealthBelowPct(50) && EnemiesInRange(8.0f) >= 3 )
					{
						me->CastSpell((Unit*)NULL, SPELL_PSYCHIC_SCREAM, false);
						events.RepeatEvent(30000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

enum eShadowPriestSpells
{
	SPELL_SILENCE		   = 65542,
	SPELL_VAMPIRIC_TOUCH	= 65490,
	SPELL_SW_PAIN		   = 65541,
	SPELL_MIND_FLAY		 = 65488,
	SPELL_MIND_BLAST		= 65492,
	SPELL_HORROR			= 65545,
	SPELL_DISPERSION		= 65544,
	SPELL_SHADOWFORM		= 16592,
};

enum eShadowPriestEvents
{
	EVENT_SPELL_SILENCE = 1,
	EVENT_SPELL_VAMPIRIC_TOUCH,
	EVENT_SPELL_SW_PAIN,
	EVENT_SPELL_MIND_FLAY,
	EVENT_SPELL_MIND_BLAST,
	EVENT_SPELL_HORROR,
	EVENT_SPELL_DISPERSION,
};

class npc_toc_shadow_priest : public CreatureScript
{
public:
	npc_toc_shadow_priest() : CreatureScript("npc_toc_shadow_priest") {}

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_shadow_priestAI (pCreature);
	}

	struct npc_toc_shadow_priestAI : public boss_faction_championsAI
	{
		npc_toc_shadow_priestAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_RANGED)
		{
			SetEquipmentSlots(false, 50040, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_SILENCE, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_VAMPIRIC_TOUCH, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_SW_PAIN, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_MIND_FLAY, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_MIND_BLAST, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_HORROR, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_DISPERSION, 10000);
			events.RescheduleEvent(EVENT_SPELL_DISPEL, urand(5000,10000));
			events.RescheduleEvent(EVENT_SPELL_PSYCHIC_SCREAM, 10000);
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) || IsCCed());
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_SILENCE:
					if( Unit* target = SelectEnemyCaster(false, 30.0f) )
					{
						me->CastSpell(target, SPELL_SILENCE, false);
						events.RepeatEvent(45000);
						EventMapGCD(events, 1500);
						break;
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_VAMPIRIC_TOUCH:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_VAMPIRIC_TOUCH, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_SW_PAIN:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_SW_PAIN, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_MIND_FLAY:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_MIND_FLAY, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_MIND_BLAST:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_MIND_BLAST, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_HORROR:
					if( me->GetVictim() && me->GetExactDist2d(me->GetVictim()) <= 30.0f )
					{
						me->CastSpell(me->GetVictim(), SPELL_HORROR, false);
						events.RepeatEvent(120000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(10000);
					break;
				case EVENT_SPELL_DISPERSION:
					if( HealthBelowPct(25) )
					{
						me->CastSpell(me, SPELL_DISPERSION, false);
						events.RepeatEvent(180000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_DISPEL:
					if( Unit* target = (urand(0,1) ? SelectTarget(SELECT_TARGET_TOPAGGRO, 0, 30.0f, true) : SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_DISPEL, 40.0f)) )
						me->CastSpell(target, SPELL_DISPEL, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_PSYCHIC_SCREAM:
					if( EnemiesInRange(8.0f) >= 3 )
					{
						me->CastSpell((Unit*)NULL, SPELL_PSYCHIC_SCREAM, false);
						events.RepeatEvent(30000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

enum eWarlockSpells
{
	SPELL_HELLFIRE			  = 65816,
	SPELL_CORRUPTION			= 65810,
	SPELL_CURSE_OF_AGONY		= 65814,
	SPELL_CURSE_OF_EXHAUSTION   = 65815,
	SPELL_FEAR				  = 65809,
	SPELL_SEARING_PAIN		  = 65819,
	SPELL_SHADOW_BOLT		   = 65821,
	SPELL_UNSTABLE_AFFLICTION   = 65812,
	SPELL_UNSTABLE_AFFLICTION_DISPEL = 65813,
	SPELL_SUMMON_FELHUNTER	  = 67514,
};

enum eWarlockEvents
{
	EVENT_SPELL_HELLFIRE = 1,
	EVENT_SPELL_CORRUPTION,
	EVENT_SPELL_CURSE_OF_AGONY,
	EVENT_SPELL_CURSE_OF_EXHAUSTION,
	EVENT_SPELL_FEAR,
	EVENT_SPELL_SEARING_PAIN,
	EVENT_SPELL_SHADOW_BOLT,
	EVENT_SPELL_UNSTABLE_AFFLICTION,
	EVENT_SPELL_SUMMON_FELHUNTER,
};

class npc_toc_warlock : public CreatureScript
{
public:
	npc_toc_warlock() : CreatureScript("npc_toc_warlock") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_warlockAI (pCreature);
	}

	struct npc_toc_warlockAI : public boss_faction_championsAI
	{
		npc_toc_warlockAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_RANGED)
		{
			SetEquipmentSlots(false, 49992, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_HELLFIRE, 10000);
			events.RescheduleEvent(EVENT_SPELL_CORRUPTION, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_CURSE_OF_AGONY, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_CURSE_OF_EXHAUSTION, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_FEAR, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_SEARING_PAIN, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_SHADOW_BOLT, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_UNSTABLE_AFFLICTION, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_SUMMON_FELHUNTER, 0);
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) || IsCCed());
		}

		void JustSummoned(Creature* c)
		{
			if( Unit* target = c->SelectNearestTarget(200.0f) )
				c->AI()->AttackStart(target);
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_SUMMON_FELHUNTER:
					DoSummon(35465, *me);
					events.PopEvent();
					break;
				case EVENT_SPELL_HELLFIRE:
					if( EnemiesInRange(9.0f) >= 3 )
					{
						me->CastSpell((Unit*)NULL, SPELL_HELLFIRE, false);
						events.RepeatEvent(30000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_CORRUPTION:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_CORRUPTION, false);
					events.RepeatEvent(urand(10000,20000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_CURSE_OF_AGONY:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_CURSE_OF_AGONY, false);
					events.RepeatEvent(urand(10000,20000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_CURSE_OF_EXHAUSTION:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_CURSE_OF_EXHAUSTION, false);
					events.RepeatEvent(urand(10000,20000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_FEAR:
					if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 20.0f, true) )
						me->CastSpell(target, SPELL_FEAR, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_SEARING_PAIN:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_SEARING_PAIN, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_SHADOW_BOLT:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_UNSTABLE_AFFLICTION:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_UNSTABLE_AFFLICTION, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

enum eMageSpells
{
	SPELL_ARCANE_BARRAGE	= 65799,
	SPELL_ARCANE_BLAST	  = 65791,
	SPELL_ARCANE_EXPLOSION  = 65800,
	SPELL_BLINK			 = 65793,
	SPELL_COUNTERSPELL	  = 65790,
	SPELL_FROST_NOVA		= 65792,
	SPELL_FROSTBOLT		 = 65807,
	SPELL_ICE_BLOCK		 = 65802,
	SPELL_POLYMORPH		 = 65801,
};

enum eMageEvents
{
	EVENT_SPELL_ARCANE_BARRAGE = 1,
	EVENT_SPELL_ARCANE_BLAST,
	EVENT_SPELL_ARCANE_EXPLOSION,
	EVENT_SPELL_BLINK,
	EVENT_SPELL_BLINK_2,
	EVENT_SPELL_COUNTERSPELL,
	EVENT_SPELL_FROSTBOLT,
	EVENT_SPELL_ICE_BLOCK,
	EVENT_SPELL_POLYMORPH,
};

class npc_toc_mage : public CreatureScript
{
public:
	npc_toc_mage() : CreatureScript("npc_toc_mage") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_mageAI (pCreature);
	}

	struct npc_toc_mageAI : public boss_faction_championsAI
	{
		npc_toc_mageAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_RANGED)
		{
			SetEquipmentSlots(false, 47524, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_ARCANE_BARRAGE, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_ARCANE_BLAST, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_ARCANE_EXPLOSION, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_BLINK, 10000);
			events.RescheduleEvent(EVENT_SPELL_COUNTERSPELL, urand(10000,20000));
			events.RescheduleEvent(EVENT_SPELL_FROSTBOLT, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_ICE_BLOCK, 10000);
			events.RescheduleEvent(EVENT_SPELL_POLYMORPH, urand(5000,10000));
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) || IsCCed());
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_ARCANE_BARRAGE:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_ARCANE_BARRAGE, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_ARCANE_BLAST:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_ARCANE_BLAST, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_ARCANE_EXPLOSION:
					if( EnemiesInRange(9.0f) >= 3 )
					{
						me->CastSpell((Unit*)NULL, SPELL_ARCANE_EXPLOSION, false);
						events.RepeatEvent(6000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_BLINK:
					if( HealthBelowPct(50) && EnemiesInRange(10.0f) >= 3 )
					{
						me->CastSpell((Unit*)NULL, SPELL_FROST_NOVA, false);
						events.RepeatEvent(15000);
						EventMapGCD(events, 1500);
						// blink disabled, fucking movement shit not working
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_COUNTERSPELL:
					if( Unit* target = SelectEnemyCaster(true, 30.0f) )
					{
						me->CastSpell(target, SPELL_COUNTERSPELL, false);
						events.RepeatEvent(24000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_FROSTBOLT:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_FROSTBOLT, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_ICE_BLOCK:
					if( HealthBelowPct(25) )
					{
						me->CastSpell(me, SPELL_ICE_BLOCK, false);
						events.RepeatEvent(300000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_POLYMORPH:
					if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f, true) )
						me->CastSpell(target, SPELL_POLYMORPH, false);
					events.RepeatEvent(15000);
					EventMapGCD(events, 1500);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

enum eHunterSpells
{
	SPELL_AIMED_SHOT		= 65883,
	SPELL_DETERRENCE		= 65871,
	SPELL_DISENGAGE		 = 65870,
	SPELL_EXPLOSIVE_SHOT	= 65866,
	SPELL_FROST_TRAP		= 65880,
	SPELL_SHOOT			 = 65868,
	SPELL_STEADY_SHOT	   = 65867,
	SPELL_WING_CLIP		 = 66207,
	SPELL_WYVERN_STING	  = 65877,
	SPELL_CALL_PET		  = 67777,
};

enum eHunterEvents
{
	EVENT_SPELL_AIMED_SHOT = 1,
	EVENT_SPELL_DETERRENCE,
	EVENT_SPELL_DISENGAGE,
	EVENT_SPELL_EXPLOSIVE_SHOT,
	EVENT_SPELL_FROST_TRAP,
	EVENT_SPELL_STEADY_SHOT,
	EVENT_SPELL_WING_CLIP,
	EVENT_SPELL_WYVERN_STING,
	EVENT_SPELL_CALL_PET,
};

class npc_toc_hunter : public CreatureScript
{
public:
	npc_toc_hunter() : CreatureScript("npc_toc_hunter") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_hunterAI (pCreature);
	}

	struct npc_toc_hunterAI : public boss_faction_championsAI
	{
		npc_toc_hunterAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_RANGED)
		{
			SetEquipmentSlots(false, 47156, EQUIP_NO_CHANGE, 48711);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_AIMED_SHOT, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_DETERRENCE, 10000);
			//events.RescheduleEvent(EVENT_SPELL_DISENGAGE, 10000);
			events.RescheduleEvent(EVENT_SPELL_EXPLOSIVE_SHOT, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_FROST_TRAP, urand(15000,20000));
			events.RescheduleEvent(EVENT_SPELL_STEADY_SHOT, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_WING_CLIP, 10000);
			events.RescheduleEvent(EVENT_SPELL_WYVERN_STING, urand(5000,15000));
			events.RescheduleEvent(EVENT_SPELL_CALL_PET, 0);
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || me->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISARM_RANGED) || IsCCed());
		}

		void JustSummoned(Creature* c)
		{
			if( Unit* target = c->SelectNearestTarget(200.0f) )
				c->AI()->AttackStart(target);
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_CALL_PET:
					DoSummon(35610, *me);
					events.PopEvent();
					break;
				case EVENT_SPELL_AIMED_SHOT:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_AIMED_SHOT, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_DETERRENCE:
					if( HealthBelowPct(25) )
					{
						me->CastSpell(me, SPELL_DETERRENCE, false);
						events.RepeatEvent(90000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_DISENGAGE:
					if( EnemiesInRange(10.0f) >= 3 )
					{
						me->CastSpell(me, SPELL_DISENGAGE, false);
						events.RepeatEvent(20000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_EXPLOSIVE_SHOT:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_EXPLOSIVE_SHOT, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_FROST_TRAP:
					me->CastSpell(me, SPELL_FROST_TRAP, false);
					events.RepeatEvent(30000);
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_STEADY_SHOT:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_STEADY_SHOT, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_WING_CLIP:
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) <= 5.0f )
						me->CastSpell(me->GetVictim(), SPELL_WING_CLIP, false);
					events.RepeatEvent(8000);
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_WYVERN_STING:
					if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 35.0f, true) )
					{
						me->CastSpell(target, SPELL_WYVERN_STING, false);
						events.RepeatEvent(60000);
						EventMapGCD(events, 1500);
						break;
					}
					events.RepeatEvent(10000);
					break;
			}

			if( me->isAttackReady() && !me->HasUnitState(UNIT_STATE_CASTING) )
			{
				me->CastSpell(me->GetVictim(), SPELL_SHOOT, true);
				me->resetAttackTimer();
			}
		}
	};
};

enum eBoomkinSpells
{
	SPELL_WRATH			 = 65862,
	SPELL_MOONFIRE		  = 65856,
	SPELL_STARFIRE		  = 65854,
	SPELL_INSECT_SWARM	  = 65855,
	SPELL_ENTANGLING_ROOTS  = 65857,
	SPELL_FAERIE_FIRE	   = 65863,
	SPELL_CYCLONE		   = 65859,
	SPELL_FORCE_OF_NATURE   = 65861,
};

enum eBoomkinEvents
{
	EVENT_SPELL_WRATH = 1,
	EVENT_SPELL_MOONFIRE,
	EVENT_SPELL_STARFIRE,
	EVENT_SPELL_INSECT_SWARM,
	EVENT_SPELL_ENTANGLING_ROOTS,
	EVENT_SPELL_FAERIE_FIRE,
	EVENT_SPELL_CYCLONE,
	EVENT_SPELL_FORCE_OF_NATURE,
};

class npc_toc_boomkin : public CreatureScript
{
public:
	npc_toc_boomkin() : CreatureScript("npc_toc_boomkin") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_boomkinAI (pCreature);
	}

	struct npc_toc_boomkinAI : public boss_faction_championsAI
	{
		npc_toc_boomkinAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_RANGED)
		{
			SetEquipmentSlots(false, 50966, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_BARKSKIN, 10000);
			events.RescheduleEvent(EVENT_SPELL_WRATH, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_MOONFIRE, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_STARFIRE, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_INSECT_SWARM, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_ENTANGLING_ROOTS, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_FAERIE_FIRE, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_CYCLONE, urand(10000,15000));
			events.RescheduleEvent(EVENT_SPELL_FORCE_OF_NATURE, urand(20000,40000));
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) || IsCCed());
		}

		void JustSummoned(Creature* c)
		{
			if( Unit* target = c->SelectNearestTarget(200.0f) )
				c->AI()->AttackStart(target);
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_BARKSKIN:
					if( HealthBelowPct(50) )
					{
						me->CastSpell(me, SPELL_BARKSKIN, false);
						events.RepeatEvent(60000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_WRATH:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_WRATH, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_MOONFIRE:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_MOONFIRE, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_STARFIRE:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_STARFIRE, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_INSECT_SWARM:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_INSECT_SWARM, false);
					events.RepeatEvent(urand(5000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_ENTANGLING_ROOTS:
					if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f, true) )
						me->CastSpell(target, SPELL_ENTANGLING_ROOTS, false);
					events.RepeatEvent(urand(10000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_FAERIE_FIRE:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_FAERIE_FIRE, false);
					events.RepeatEvent(urand(15000,20000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_CYCLONE:
					if( Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 20.0f, true) )
						me->CastSpell(target, SPELL_CYCLONE, false);
					events.RepeatEvent(urand(25000,40000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_FORCE_OF_NATURE:
					me->CastSpell((Unit*)NULL, SPELL_FORCE_OF_NATURE, false);
					events.RepeatEvent(180000);
					EventMapGCD(events, 1500);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

enum eWarriorSpells
{
	SPELL_BLADESTORM			= 65947,
	SPELL_INTIMIDATING_SHOUT	= 65930,
	SPELL_MORTAL_STRIKE		 = 65926,
	SPELL_CHARGE				= 68764,
	SPELL_DISARM				= 65935,
	SPELL_OVERPOWER			 = 65924,
	SPELL_SUNDER_ARMOR		  = 65936,
	SPELL_SHATTERING_THROW	  = 65940,
	SPELL_RETALIATION		   = 65932,
};

enum eWarriorEvents
{
	EVENT_SPELL_BLADESTORM = 1,
	EVENT_SPELL_INTIMIDATING_SHOUT,
	EVENT_SPELL_MORTAL_STRIKE,
	EVENT_SPELL_CHARGE,
	EVENT_SPELL_DISARM,
	EVENT_SPELL_OVERPOWER,
	EVENT_SPELL_SUNDER_ARMOR,
	EVENT_SPELL_SHATTERING_THROW,
	EVENT_SPELL_RETALIATION,
};

class npc_toc_warrior : public CreatureScript
{
public:
	npc_toc_warrior() : CreatureScript("npc_toc_warrior") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_warriorAI (pCreature);
	}

	struct npc_toc_warriorAI : public boss_faction_championsAI
	{
		npc_toc_warriorAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_MELEE)
		{
			SetEquipmentSlots(false, 47427, 46964, EQUIP_NO_CHANGE);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_BLADESTORM, 20000);
			events.RescheduleEvent(EVENT_SPELL_INTIMIDATING_SHOUT, 14000);
			events.RescheduleEvent(EVENT_SPELL_MORTAL_STRIKE, urand(5000,10000));
			events.RescheduleEvent(EVENT_SPELL_CHARGE, 3000);
			events.RescheduleEvent(EVENT_SPELL_DISARM, urand(15000,25000));
			events.RescheduleEvent(EVENT_SPELL_OVERPOWER, urand(5000,10000));
			events.RescheduleEvent(EVENT_SPELL_SUNDER_ARMOR, urand(5000,10000));
			events.RescheduleEvent(EVENT_SPELL_SHATTERING_THROW, urand(25000,40000));
			events.RescheduleEvent(EVENT_SPELL_RETALIATION, urand(25000,40000));
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || IsCCed());
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_BLADESTORM:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( EnemiesInRange(8.0f) >= 3 )
					{
						me->CastSpell(me, SPELL_BLADESTORM, false);
						events.RepeatEvent(90000);
						events.DelayEvents(9000);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_INTIMIDATING_SHOUT:
					if( EnemiesInRange(8.0f) >= 3 )
					{
						me->CastSpell((Unit*)NULL, SPELL_INTIMIDATING_SHOUT, false);
						events.RepeatEvent(120000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_MORTAL_STRIKE:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_MORTAL_STRIKE, false);
					events.RepeatEvent(urand(6000,8000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_CHARGE:
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) > 8.0f && me->GetDistance2d(me->GetVictim()) < 25.0f )
					{
						me->CastSpell(me->GetVictim(), SPELL_CHARGE, false);
						events.RepeatEvent(10000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_DISARM:
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) < 5.0f  )
					{
						me->CastSpell(me->GetVictim(), SPELL_DISARM, false);
						events.RepeatEvent(60000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_OVERPOWER:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) < 5.0f  )
					{
						me->CastSpell(me->GetVictim(), SPELL_OVERPOWER, false);
						events.RepeatEvent(urand(10000,15000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_SUNDER_ARMOR:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) < 5.0f  )
					{
						me->CastSpell(me->GetVictim(), SPELL_SUNDER_ARMOR, false);
						events.RepeatEvent(urand(10000,15000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_SHATTERING_THROW:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) < 25.0f  )
					{
						me->CastSpell(me->GetVictim(), SPELL_SHATTERING_THROW, false);
						events.RepeatEvent(300000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_RETALIATION:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( EnemiesInRange(8.0f) >= 3 )
					{
						me->CastSpell(me, SPELL_RETALIATION, false);
						events.RepeatEvent(300000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

enum eDeathKnightSpells
{
	SPELL_CHAINS_OF_ICE	   = 66020,
	SPELL_DEATH_COIL		  = 66019,
	SPELL_DEATH_GRIP		  = 66017,
	SPELL_FROST_STRIKE		= 66047,
	SPELL_ICEBOUND_FORTITUDE  = 66023,
	SPELL_ICY_TOUCH		   = 66021,
	SPELL_STRANGULATE		 = 66018,
};

enum eDeathKnightEvents
{
	EVENT_SPELL_CHAINS_OF_ICE = 1,
	EVENT_SPELL_DEATH_COIL,
	EVENT_SPELL_DEATH_GRIP,
	EVENT_SPELL_FROST_STRIKE,
	EVENT_SPELL_ICEBOUND_FORTITUDE,
	EVENT_SPELL_ICY_TOUCH,
	EVENT_SPELL_STRANGULATE,
};

class npc_toc_dk : public CreatureScript
{
public:
	npc_toc_dk() : CreatureScript("npc_toc_dk") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_dkAI (pCreature);
	}

	struct npc_toc_dkAI : public boss_faction_championsAI
	{
		npc_toc_dkAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_MELEE)
		{
			SetEquipmentSlots(false, 47518, 51021, EQUIP_NO_CHANGE);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_CHAINS_OF_ICE, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_DEATH_COIL, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_DEATH_GRIP, 0);
			events.RescheduleEvent(EVENT_SPELL_FROST_STRIKE, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_ICEBOUND_FORTITUDE, 10000);
			events.RescheduleEvent(EVENT_SPELL_ICY_TOUCH, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_STRANGULATE, urand(20000,30000));
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || IsCCed());
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_CHAINS_OF_ICE:
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) <= 25.0f )
					{
						me->CastSpell(me->GetVictim(), SPELL_CHAINS_OF_ICE, false);
						events.RepeatEvent(urand(10000,15000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_DEATH_COIL:
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) <= 30.0f )
					{
						me->CastSpell(me->GetVictim(), SPELL_DEATH_COIL, false);
						events.RepeatEvent(urand(5000,8000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_DEATH_GRIP:
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) <= 30.0f && me->GetDistance2d(me->GetVictim()) >= 12.0f )
					{
						me->CastSpell(me->GetVictim(), SPELL_DEATH_GRIP, false);
						Position pos;
						float x, y, z;
						me->GetClosePoint(x,y,z,3.0f);
						pos.Relocate(x, y, z);
						me->GetVictim()->CastSpell(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), 49575, true);
						events.RepeatEvent(35000);
						EventMapGCD(events, 2000);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_FROST_STRIKE:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) < 5.0f  )
					{
						me->CastSpell(me->GetVictim(), SPELL_FROST_STRIKE, false);
						events.RepeatEvent(urand(6000,10000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_ICEBOUND_FORTITUDE:
					if( HealthBelowPct(50) )
					{
						me->CastSpell(me, SPELL_ICEBOUND_FORTITUDE, false);
						events.RepeatEvent(60000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_ICY_TOUCH:
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) < 20.0f  )
					{
						me->CastSpell(me->GetVictim(), SPELL_ICY_TOUCH, false);
						events.RepeatEvent(urand(10000,15000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_STRANGULATE:
					if( Unit* target = SelectEnemyCaster(false, 30.0f) )
					{
						me->CastSpell(me->GetVictim(), SPELL_STRANGULATE, false);
						events.RepeatEvent(120000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

enum eRogueSpells
{
	SPELL_FAN_OF_KNIVES		 = 65955,
	SPELL_BLIND				 = 65960,
	SPELL_CLOAK				 = 65961,
	SPELL_BLADE_FLURRY		  = 65956,
	SPELL_SHADOWSTEP			= 66178,
	SPELL_HEMORRHAGE			= 65954,
	SPELL_EVISCERATE			= 65957,
};

enum eRogueEvents
{
	EVENT_SPELL_FAN_OF_KNIVES = 1,
	EVENT_SPELL_BLIND,
	EVENT_SPELL_CLOAK,
	EVENT_SPELL_BLADE_FLURRY,
	EVENT_SPELL_SHADOWSTEP,
	EVENT_SPELL_HEMORRHAGE,
	EVENT_SPELL_EVISCERATE,
};

class npc_toc_rogue : public CreatureScript
{
public:
	npc_toc_rogue() : CreatureScript("npc_toc_rogue") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_rogueAI (pCreature);
	}

	struct npc_toc_rogueAI : public boss_faction_championsAI
	{
		npc_toc_rogueAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_MELEE)
		{
			SetEquipmentSlots(false, 47422, 49982, EQUIP_NO_CHANGE);
			me->setPowerType(POWER_ENERGY);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_FAN_OF_KNIVES, 10000);
			events.RescheduleEvent(EVENT_SPELL_BLIND, urand(10000,15000));
			events.RescheduleEvent(EVENT_SPELL_CLOAK, 10000);
			events.RescheduleEvent(EVENT_SPELL_BLADE_FLURRY, urand(20000,40000));
			//events.RescheduleEvent(EVENT_SPELL_SHADOWSTEP, urand(15000,25000));
			events.RescheduleEvent(EVENT_SPELL_HEMORRHAGE, urand(3000,5000));
			events.RescheduleEvent(EVENT_SPELL_EVISCERATE, urand(20000,25000));
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || IsCCed());
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_FAN_OF_KNIVES:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( EnemiesInRange(10.0f) >= 3 )
					{
						me->CastSpell(me->GetVictim(), SPELL_FAN_OF_KNIVES, false);
						events.RepeatEvent(urand(6000,10000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_BLIND:
					if( Unit* target = SelectTarget(SELECT_TARGET_BOTTOMAGGRO, 0, 20.0f, true) )
					{
						me->CastSpell(target, SPELL_BLIND, false);
						events.RepeatEvent(120000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_CLOAK:
					if( HealthBelowPct(50) )
					{
						me->CastSpell(me, SPELL_CLOAK, false);
						events.RepeatEvent(90000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(6000);
					break;
				case EVENT_SPELL_BLADE_FLURRY:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
						events.RepeatEvent(5000);
					else
					{
						me->CastSpell(me, SPELL_BLADE_FLURRY, false);
						events.RepeatEvent(120000);
						EventMapGCD(events, 1500);
					}
					break;
				case EVENT_SPELL_SHADOWSTEP:
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) < 40.0f && me->GetDistance2d(me->GetVictim()) > 10.0f )
					{
						me->CastSpell(me->GetVictim(), SPELL_SHADOWSTEP, false);
						events.RepeatEvent(30000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_HEMORRHAGE:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) <= 5.0f )
					{
						me->CastSpell(me->GetVictim(), SPELL_HEMORRHAGE, false);
						events.RepeatEvent(5000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_EVISCERATE:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( me->GetVictim() && me->GetDistance2d(me->GetVictim()) <= 5.0f )
					{
						me->CastSpell(me->GetVictim(), SPELL_EVISCERATE, false);
						events.RepeatEvent(urand(15000,25000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};

};

enum eEnhShamanSpells
{
	SPELL_EARTH_SHOCK_ENH   = 65973,
	SPELL_LAVA_LASH		 = 65974,
	SPELL_STORMSTRIKE	   = 65970,
	SPELL_GROUNDING_TOTEM	= 65989,
	SPELL_WINDFURY_TOTEM	= 65990,
	SPELL_TREMOR_TOTEM		= 65992,
};

enum eEnhShamanEvents
{
	EVENT_SPELL_EARTH_SHOCK_ENH = 201,
	EVENT_SPELL_LAVA_LASH,
	EVENT_SPELL_STORMSTRIKE,
	EVENT_SUMMON_TOTEM,
};

class npc_toc_enh_shaman : public CreatureScript
{
public:
	npc_toc_enh_shaman() : CreatureScript("npc_toc_enh_shaman") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_enh_shamanAI (pCreature);
	}

	struct npc_toc_enh_shamanAI : public boss_faction_championsAI
	{
		npc_toc_enh_shamanAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_MELEE)
		{
			SetEquipmentSlots(false, 51803, 48013, EQUIP_NO_CHANGE);
			me->SetModifierValue(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, 1.0f);
			me->UpdateDamagePhysical(OFF_ATTACK);

			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_HEROISM_OR_BLOODLUST, urand(25000,40000));
			events.RescheduleEvent(EVENT_SPELL_EARTH_SHOCK_ENH, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_LAVA_LASH, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_STORMSTRIKE, urand(3000,10000));
			events.RescheduleEvent(EVENT_SUMMON_TOTEM, urand(10000,20000));
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || IsCCed());
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_EARTH_SHOCK_ENH:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 0, 25.0f, true) )
					{
						me->CastSpell(target, SPELL_EARTH_SHOCK_ENH, false);
						events.RepeatEvent(urand(6000,8000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_LAVA_LASH:
					if( me->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISARM_OFFHAND) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 0, 5.0f, true) )
					{
						me->CastSpell(target, SPELL_LAVA_LASH, false);
						events.RepeatEvent(urand(6000,8000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_STORMSTRIKE:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) && me->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISARM_OFFHAND) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 0, 5.0f, true) )
					{
						me->CastSpell(target, SPELL_STORMSTRIKE, false);
						events.RepeatEvent(urand(8000,9000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_HEROISM_OR_BLOODLUST:
					if( me->GetEntry() == NPC_ALLIANCE_SHAMAN_RESTORATION )
						me->CastSpell((Unit*)NULL, SPELL_HEROISM, true);
					else
						me->CastSpell((Unit*)NULL, SPELL_BLOODLUST, true);
					events.RepeatEvent(600000);
					EventMapGCD(events, 1500);
					break;
				case EVENT_SUMMON_TOTEM:
					me->CastSpell((Unit*)NULL, RAND(SPELL_GROUNDING_TOTEM,SPELL_WINDFURY_TOTEM,SPELL_TREMOR_TOTEM), false);
					events.RepeatEvent(30000);
					EventMapGCD(events, 1500);
					break;

			}

			DoMeleeAttackIfReady();
		}
	};
};

enum eRetroPaladinSpells
{
	SPELL_AVENGING_WRATH		= 66011,
	SPELL_CRUSADER_STRIKE	   = 66003,
	SPELL_DIVINE_SHIELD		 = 66010,
	SPELL_DIVINE_STORM		  = 66006,
	SPELL_HAMMER_OF_JUSTICE_RET = 66007,
	SPELL_HAND_OF_PROTECTION_RET = 66009,
	SPELL_JUDGEMENT_OF_COMMAND  = 66005,
	SPELL_REPENTANCE			= 66008,
	SPELL_SEAL_OF_COMMAND	   = 66004,
};

enum eRetroPaladinEvents
{
	EVENT_SPELL_AVENGING_WRATH = 1,
	EVENT_SPELL_CRUSADER_STRIKE,
	EVENT_SPELL_DIVINE_SHIELD,
	EVENT_SPELL_DIVINE_STORM,
	EVENT_SPELL_HAMMER_OF_JUSTICE_RET,
	EVENT_SPELL_HAND_OF_PROTECTION_RET,
	EVENT_SPELL_JUDGEMENT_OF_COMMAND,
	EVENT_SPELL_REPENTANCE,
};

class npc_toc_retro_paladin : public CreatureScript
{
public:
	npc_toc_retro_paladin() : CreatureScript("npc_toc_retro_paladin") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_retro_paladinAI (pCreature);
	}

	struct npc_toc_retro_paladinAI : public boss_faction_championsAI
	{
		npc_toc_retro_paladinAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_MELEE)
		{
			SetEquipmentSlots(false, 47519, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_AVENGING_WRATH, urand(20000,30000));
			events.RescheduleEvent(EVENT_SPELL_CRUSADER_STRIKE, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_DIVINE_SHIELD, 10000);
			events.RescheduleEvent(EVENT_SPELL_DIVINE_STORM, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_HAMMER_OF_JUSTICE_RET, urand(15000,25000));
			events.RescheduleEvent(EVENT_SPELL_HAND_OF_PROTECTION_RET, urand(25000,40000));
			events.RescheduleEvent(EVENT_SPELL_JUDGEMENT_OF_COMMAND, urand(3000,10000));
			events.RescheduleEvent(EVENT_SPELL_REPENTANCE, urand(10000,15000));
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasUnitState(UNIT_STATE_CASTING) || IsCCed());
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_AVENGING_WRATH:
					me->CastSpell(me, SPELL_AVENGING_WRATH, false);
					events.RepeatEvent(180000);
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_CRUSADER_STRIKE:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 0, 5.0f, true) )
					{
						me->CastSpell(target, SPELL_CRUSADER_STRIKE, false);
						events.RepeatEvent(urand(6000,8000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_DIVINE_SHIELD:
					if( HealthBelowPct(25) )
					{
						me->CastSpell(me, SPELL_DIVINE_SHIELD, false);
						events.RepeatEvent(300000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_DIVINE_STORM:
					if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						events.RepeatEvent(5000);
						break;
					}
					if( EnemiesInRange(5.0f) >= 3 )
					{
						me->CastSpell((Unit*)NULL, SPELL_DIVINE_STORM, false);
						events.RepeatEvent(urand(10000,15000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_HAMMER_OF_JUSTICE_RET:
					if( Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 0, 15.0f, true) )
					{
						me->CastSpell(target, SPELL_HAMMER_OF_JUSTICE_RET, false);
						events.RepeatEvent(40000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_HAND_OF_PROTECTION_RET:
					if( Creature* target = SelectTarget_MostHPLostFriendlyMissingBuff(SPELL_HAND_OF_PROTECTION_RET, 30.0f) )
					{
						me->CastSpell(target, SPELL_HAND_OF_PROTECTION_RET, false);
						events.RepeatEvent(300000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_JUDGEMENT_OF_COMMAND:
					if( Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 0, 20.0f, true) )
					{
						me->CastSpell(target, SPELL_JUDGEMENT_OF_COMMAND, false);
						events.RepeatEvent(urand(10000,15000));
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
				case EVENT_SPELL_REPENTANCE:
					if( Unit* target = SelectTarget(SELECT_TARGET_BOTTOMAGGRO, 0, 20.0f, true) )
					{
						me->CastSpell(target, SPELL_REPENTANCE, false);
						events.RepeatEvent(60000);
						EventMapGCD(events, 1500);
					}
					else
						events.RepeatEvent(5000);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

enum eWarlockPetSpells
{
	SPELL_DEVOUR_MAGIC  = 67518,
	SPELL_SPELL_LOCK  = 67519,
};

enum eWarlockPetEvents
{
	EVENT_SPELL_DEVOUR_MAGIC = 1,
	EVENT_SPELL_SPELL_LOCK,
};

class npc_toc_pet_warlock : public CreatureScript
{
public:
	npc_toc_pet_warlock() : CreatureScript("npc_toc_pet_warlock") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_pet_warlockAI (pCreature);
	}

	struct npc_toc_pet_warlockAI : public boss_faction_championsAI
	{
		npc_toc_pet_warlockAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_PET)
		{
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_DEVOUR_MAGIC, urand(5000,15000));
			events.RescheduleEvent(EVENT_SPELL_SPELL_LOCK, urand(5000,15000));
		}

		EventMap events;

		bool myCanCast()
		{
			return !(me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) || IsCCed());
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_DEVOUR_MAGIC:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_DEVOUR_MAGIC, false);
					events.RepeatEvent(urand(8000,15000));
					EventMapGCD(events, 1500);
					break;
				case EVENT_SPELL_SPELL_LOCK:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_SPELL_LOCK, false);
					events.RepeatEvent(24000);
					EventMapGCD(events, 1500);
					break;
			}

			DoMeleeAttackIfReady();
		}

		void EnterEvadeMode()
		{
			me->DespawnOrUnsummon();
		}
	};

};

enum eHunterPetSpells
{
	SPELL_CLAW  = 67793,
};

enum eHunterPetEvents
{
	EVENT_SPELL_CLAW = 1,
};

class npc_toc_pet_hunter : public CreatureScript
{
public:
	npc_toc_pet_hunter() : CreatureScript("npc_toc_pet_hunter") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_toc_pet_hunterAI (pCreature);
	}

	struct npc_toc_pet_hunterAI : public boss_faction_championsAI
	{
		npc_toc_pet_hunterAI(Creature *pCreature) : boss_faction_championsAI(pCreature, AI_PET)
		{
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_CLAW, urand(5000,15000));
		}

		EventMap events;

		bool myCanCast()
		{
			return !IsCCed();
		}

		void UpdateAI(uint32 diff)
		{
			boss_faction_championsAI::UpdateAI(diff);
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( !myCanCast() )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_CLAW:
					if( me->GetVictim() )
						me->CastSpell(me->GetVictim(), SPELL_CLAW, false);
					events.RepeatEvent(urand(8000,15000));
					break;
			}

			DoMeleeAttackIfReady();
		}

		void EnterEvadeMode()
		{
			me->DespawnOrUnsummon();
		}
	};
};

class go_toc_champions_cache : public GameObjectScript
{
public:
	go_toc_champions_cache() : GameObjectScript("go_toc_champions_cache") { }

	bool OnGossipHello(Player* plr, GameObject* go)
	{
		if (plr->IsGameMaster())
			return false;

		if (!go->loot.items.size())
			return false;

		uint8 invalidCount = 0;
		for( std::vector<LootItem>::iterator itr = go->loot.items.begin(); itr != go->loot.items.end(); ++itr )
			if( ItemTemplate const *iProto = sObjectMgr->GetItemTemplate((*itr).itemid) )
				if( ((iProto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) && plr->GetTeamId() != TEAM_HORDE) || ((iProto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) && plr->GetTeamId() != TEAM_ALLIANCE) )
					if (!((*itr).is_looted))
					{
						(*itr).count = 0;
						(*itr).is_looted = true;
						if (go->loot.unlootedCount)
							--go->loot.unlootedCount;
					}

		return false;
	}
};

class spell_faction_champion_warl_unstable_affliction : public SpellScriptLoader
{
    public:
        spell_faction_champion_warl_unstable_affliction() : SpellScriptLoader("spell_faction_champion_warl_unstable_affliction") { }

        class spell_faction_champion_warl_unstable_affliction_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_faction_champion_warl_unstable_affliction_AuraScript);

            bool Validate(SpellInfo const* /*spell*/)
            {
                if (!sSpellMgr->GetSpellInfo(SPELL_UNSTABLE_AFFLICTION_DISPEL))
                    return false;
                return true;
            }

            void HandleDispel(DispelInfo* dispelInfo)
            {
                if (Unit* caster = GetCaster())
                    caster->CastSpell(dispelInfo->GetDispeller(), SPELL_UNSTABLE_AFFLICTION_DISPEL, true, NULL, GetEffect(EFFECT_0));
            }

            void Register()
            {
                AfterDispel += AuraDispelFn(spell_faction_champion_warl_unstable_affliction_AuraScript::HandleDispel);
            }
        };

        AuraScript* GetAuraScript() const
        {
            return new spell_faction_champion_warl_unstable_affliction_AuraScript();
        }
};

void AddSC_boss_faction_champions()
{
	new npc_toc_druid();
	new npc_toc_shaman();
	new npc_toc_paladin();
	new npc_toc_priest();
	new npc_toc_shadow_priest();
	new npc_toc_mage();
	new npc_toc_warlock();
	new npc_toc_hunter();
	new npc_toc_boomkin();
	new npc_toc_warrior();
	new npc_toc_dk();
	new npc_toc_rogue();
	new npc_toc_enh_shaman();
	new npc_toc_retro_paladin();
	new npc_toc_pet_warlock();
	new npc_toc_pet_hunter();
	new go_toc_champions_cache();
	new spell_faction_champion_warl_unstable_affliction();
}
